package utp.tepc.jogogeneral;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.*;

public class ViewJogo
{
    public static final int LEN_CHAR_SEPARATOR = 120;

    private String opMarcar = "mM";
    private String opLancar = "Ll";
    private String opSelecionar = "Ss";
    private String opVer = "Vv";
    private String opQueimar = "Qq";
    private String opAnalisarLanc = "Aa";

    private Jogo jogo;
    private InputStreamReader reader;
    private BufferedReader buffer;

    public ViewJogo()
    {
	this.reader = new InputStreamReader(System.in);
	this.buffer = new BufferedReader(this.reader);
    }

    public void setJogo(Jogo j)
    {
	jogo = j;
    }

    public void viewAddJogadores() throws Exception
    {
	ViewJogo.drawCharLine('+', LEN_CHAR_SEPARATOR, true);
	System.out.println("Informe o nome dos jogadores.");
	System.out.println("Digite (S)air quando terminar de inserir os nome");
	ViewJogo.drawCharLine('+', LEN_CHAR_SEPARATOR, true);

	String op = " ";

	while (!isValidValue("Ss".toCharArray(), op.toCharArray()[0]))
	{
	    op = buffer.readLine();
	    jogo.adcionarJogadorDado(new JogadorDado(op));
	}

	jogo.getJogadores().remove(jogo.getJogadores().size() - 1);

	this.header();

	ViewJogo.drawCharLine('+', LEN_CHAR_SEPARATOR, true);
	System.out.println("Os seguinte jogadores foram adicionados: ");
	for (JogadorDado j : jogo.getJogadores())
	{
	    System.out.println(j.toString() + "\n");
	}
	ViewJogo.drawCharLine('+', LEN_CHAR_SEPARATOR, true);
    }

    public boolean telaInicar(String nomeJogo) throws Exception
    {
	if (nomeJogo == "GENERAL")
	{
	    this.telaInicalGeneral();
	    return true;
	}

	System.out.println("O jogo selecionado não existe.");
	return false;
    }

    public void header() throws IOException
    {
	ViewJogo.drawCharLine('*', ViewJogo.LEN_CHAR_SEPARATOR, true);
	System.out.println("JOGO: GENERAL");
	ViewJogo.drawCharLine('*', ViewJogo.LEN_CHAR_SEPARATOR, true);
    }

    private void telaInicalGeneral() throws Exception
    {
	this.header();
	this.viewAddJogadores();
	
	jogo.iniciarJogo();

	while (jogo.fimJogo())
	{
	    this.titleRodada();
	    this.telaAcoesJogador();
	}

	ViewJogo.drawCharLine('*', ViewJogo.LEN_CHAR_SEPARATOR, true);
	System.out.println("FIM DE JOGO");
	ViewJogo.drawCharLine('*', ViewJogo.LEN_CHAR_SEPARATOR, true);
	System.out.printf("%15s | %10s", "Nome", "Pontuacao");
	System.out.println();
	for (String s : this.jogo.getPlacar().keySet())
	{
	    System.out.printf("%15s | %10s", s, this.jogo.getPlacar().get(s).toString());
	    System.out.println();
	}
	ViewJogo.drawCharLine('*', ViewJogo.LEN_CHAR_SEPARATOR, true);
	System.out.println("O VENCEDOR: " + this.jogo.getVencedor(this.jogo.getPlacar()));
	ViewJogo.drawCharLine('*', ViewJogo.LEN_CHAR_SEPARATOR, true);
    }

    public void telaAcoesJogador() throws Exception
    {
	char op;
	jogo.setDadosJogador();

	while (!jogo.jogadaConcluida())
	{
	    op = this.menuAcoesIniciais();

	    if (isValidValue(this.opLancar.toCharArray(), op))
	    {
		this.jogo.jogadorLanca();
		this.titleResultLancRealizado(this.jogo.getJogadorAtual());
		this.titleJogadasPossiveis();
	    }

	    if (isValidValue(this.opSelecionar.toCharArray(), op))
	    {
		this.telaSelecionarDados();
	    }

	    if (isValidValue(this.opMarcar.toCharArray(), op))
	    {
		String jogada = this.telaMarcarPonto(jogo.getJogadorAtual());

		if (jogada != "V")
		{
		    HashMap<String, Integer> jogadas = this.jogo.getGeneral().getJogadas();
		    boolean result = this.jogo.marcarPontos(this.jogo.getJogadorAtual(), jogada, jogadas.get(jogada));

		    if (!result)
		    {
			System.out.println("******************************************************************* ");
			System.out.println("************* NÃO É POSSIVEL MARCAR ESSA JOGADA *******************");
			System.out.println("      Essa jogada já foi marcada, escolha outra jogada");
			System.out.println("       para marcar ou queime uma opação de seu placar.");
			System.out.println("******************************************************************* ");
			System.out.println("******************************************************************* ");
		    }
		}
	    }

	    if (isValidValue(this.opQueimar.toCharArray(), op))
	    {
		String jogada = this.telaQueimaJogada(this.jogo.getJogadorAtual());

		if (jogada != "V")
		{
		    boolean result = this.jogo.marcarPontos(this.jogo.getJogadorAtual(), jogada, (Integer) 0);

		    if (!result)
		    {
			System.out.println("******************************************************************* ");
			System.out.println("************* NÃO É POSSIVEL MARCAR ESSA JOGADA *******************");
			System.out.println("      Essa jogada já foi marcada, escolha outra jogada");
			System.out.println("       para marcar ou queime uma opação de seu placar.");
			System.out.println("******************************************************************* ");
			System.out.println("******************************************************************* ");
		    }
		}
	    }

	    if (isValidValue(this.opVer.toCharArray(), op))
	    {
		this.telaJogadasRealizadas(this.jogo.getJogadorAtual());
	    }

	    if (isValidValue(this.opAnalisarLanc.toCharArray(), op))
	    {
		this.titleResultLancRealizado(this.jogo.getJogadorAtual());
	    }
	}
    }

    public void telaSelecionarDados() throws Exception
    {
	String opNumValidos = "12345";
	String opValidSair = "Ss";

	ViewJogo.drawCharLine(':', 120, true);
	List<Dado> ds = jogo.getJogadorAtual().getDados();

	this.titleResultLancRealizado(this.jogo.getJogadorAtual());

	System.out.println("Digite o número que está entre colchetes.");
	System.out.println("(1-5)Digite o número dos dados que irão ficar na mesa.");
	System.out.println("(S)air para finalizar a seleção.");

	char op = ' ';
	while (!isValidValue(opValidSair.toCharArray(), op))
	{
	    op = entradaDados((opNumValidos + opValidSair).toCharArray());

	    if (isValidValue(opNumValidos.toCharArray(), op))
	    {
		int i = Integer.parseInt(Character.toString(op)) - 1;
		ds.get(i).setSeleciondao(true);
	    }
	}

	ViewJogo.drawCharLine(':', 120, true);
    }

    public String telaMarcarPonto(JogadorDado j) throws Exception
    {
	StringBuilder sbOpValids = new StringBuilder();
	HashMap<String, Integer> jogadas = this.jogo.getGeneral().getJogadas();
	String[] jogadasNome = new String[jogadas.size()];

	int i = 0;
	for (String key : jogadas.keySet())
	    jogadasNome[i++] = key;

	for (int x = 1; x <= jogadasNome.length; x++)
	    sbOpValids.append(x);

	sbOpValids.append("Vv");

	char op = ' ';

	this.titleResultLancRealizado(this.jogo.getJogadorAtual());
	this.titleJogadasPossiveis();
	System.out.println("Digite o número que está entre colchetes para marcar a opção desejada.");
	System.out.println("Digite (V)oltar.");
	op = entradaDados(sbOpValids.toString().toCharArray());

	if (isValidValue("Vv".toCharArray(), op))
	    return "V";

	return jogadasNome[Integer.parseInt(Character.toString(op)) - 1];
    }
    private String telaQueimaJogada(JogadorDado j) throws Exception
    {
	Pattern p = Pattern.compile("^( :: )");
	Matcher m;

	StringBuilder sbOpValids = new StringBuilder();
	StringBuilder sbOpsJogadas = new StringBuilder();
	HashMap<String, Integer> jogadas = this.jogo.getGeneral().getJogadaParaQueimar(j.getPontuacao());
	String[] jogadasNome = new String[jogadas.size()];

	int i = 0;
	for (String key : jogadas.keySet())
	{
	    sbOpsJogadas.append(" :: [");
	    sbOpsJogadas.append(i);
	    sbOpsJogadas.append("] ");
	    sbOpsJogadas.append(key);
	    jogadasNome[i++] = key;
	}

	m = p.matcher(sbOpsJogadas.toString());

	for (int x = 0; x < jogadasNome.length; x++)
	    sbOpValids.append(x);

	sbOpValids.append("Vv");

	char op = ' ';

	System.out.println(m.replaceAll(""));
	System.out.println("Digite o número que está entre colchetes para queimar a opção desejada.");
	System.out.println("Digite o número da jogada que será queimada.");
	System.out.println("Digite (V)oltar.");
	op = entradaDados(sbOpValids.toString().toCharArray());

	if (isValidValue("Vv".toCharArray(), op))
	    return "V";

	return jogadasNome[Integer.parseInt(Character.toString(op))];
    }

    public void telaJogadasRealizadas(JogadorDado j)
    {
	ViewJogo.drawCharLine('-', 120, true);
	System.out.println("Jogadas realizado de: " + j.toString());
	ViewJogo.drawCharLine('-', 120, true);

	HashMap<String, Integer> pontos = j.getPontuacao();
	ViewJogo.drawCharLine('-', 120, true);
	System.out.printf("%17s | %10s", "Nome da jogadada", "Ponto");
	System.out.println();
	ViewJogo.drawCharLine('-', 120, true);
	for (String key : pontos.keySet())
	{
	    System.out.printf("%17s | %10s", key, pontos.get(key));
	    System.out.println();
	    ViewJogo.drawCharLine('-', 120, true);
	}
	ViewJogo.drawCharLine('-', 120, true);
    }

    public char menuAcoesIniciais() throws IOException
    {
	this.titleInfosJogador();
	ViewJogo.drawCharLine('/', 120, true);
	System.out.println("------------------------------- MENU ------------------------------------------");
	ViewJogo.drawCharLine('/', 120, true);
	System.out.print(this.buildMenuItens());
	ViewJogo.drawCharLine('/', 120, true);

	String opPossiveis;
	if (this.jogo.temLances())
	    opPossiveis = this.opLancar + this.opMarcar + this.opQueimar + this.opSelecionar + this.opVer + this.opAnalisarLanc;
	else
	    opPossiveis = this.opMarcar + this.opQueimar + this.opVer + this.opAnalisarLanc;

	return entradaDados(opPossiveis.toCharArray());
    }

    public void titleRodada()
    {
	ViewJogo.drawCharLine('+', 120, true);
	System.out.println("Jogando a Rodada: " + this.jogo.getRodada());
	ViewJogo.drawCharLine('+', 120, true);
    }

    public void titleResultLancRealizado(JogadorDado j) throws Exception
    {
	ViewJogo.drawCharLine('+', 120, true);
	System.out.print("Jogador: " + j.toString() + " | Resultado do lançamento [" + j.getQtdLances() + "] ");
	System.out.println(jogo.getValoresLanc());
	ViewJogo.drawCharLine('+', 120, true);
    }

    public void titleInfosJogador()
    {
	ViewJogo.drawCharLine('+', 120, true);
	System.out.println("Agora jogando: " + jogo.getJogadorAtual().toString());
	ViewJogo.drawCharLine('+', 120, true);
    }

    public void titleJogadasPossiveis() throws Exception
    {
	HashMap<String, Integer> jogadas = this.jogo.getGeneral().getJogadas();
	StringBuilder sb = new StringBuilder();
	int count = 1;

	Pattern p = Pattern.compile("( :: )$");
	Matcher m;

	for (String s : jogadas.keySet())
	{
	    sb.append("[");
	    sb.append(count);
	    sb.append("] ");
	    sb.append(jogadas.get(s));
	    sb.append(" pontos com ");
	    sb.append(s);
	    sb.append(" :: ");
	    count++;
	}

	m = p.matcher(sb.toString());

	System.out.println("Jogadas possíveis: ");// + jogo.getJogadorAtual().toString());
	System.out.println(m.replaceAll(""));
    }

    private String buildMenuItens()
    {
	StringBuilder sbTemLances = new StringBuilder();
	StringBuilder sbNaoTemLances = new StringBuilder();
	StringBuilder sbAmbos = new StringBuilder();

	sbTemLances.append("Digite: (L)ancar para fazer um novo lançamento com todos os dados.\n");

	if (jogo.getQtdLance() > 0)
	{
	    sbTemLances.append("Digite: (S)elecionar dados para fica na mesa e realizar outro lancamento.\n");
	    sbTemLances.append("Digite: (V)isualizar para ver as jogadas realizadas.\n");

	    sbNaoTemLances.append("Digite: (M)arcar os pontos.\n");
	    sbNaoTemLances.append("Digite: (Q)ueimar jogada.\n");
	    sbAmbos.append("Digite: (A)analisar os dados do lancamento realizado.\n");
	}

	String result = new String();

	if (this.jogo.temLances())
	    result = sbTemLances.toString() + sbNaoTemLances.toString() + sbAmbos.toString();
	else
	    result = sbNaoTemLances.toString() + sbAmbos.toString();

	return result;
    }

    public static void drawCharLine(char c, int lineLength)
    {
	for (int i = 0; i < lineLength; i++)
	    System.out.print(c);
    }

    public static void drawCharLine(char c, int lineLength, boolean breakLine)
    {
	drawCharLine(c, lineLength);
	System.out.println();
    }

    private char entradaDados(char[] valoresPossiveis) throws IOException
    {
	char value;
	boolean flag = true;

	do
	{
	    String c = buffer.readLine();

	    if (c.length() == 0)
		value = ' ';
	    else
		value = c.toCharArray()[0];

	    if (!isValidValue(valoresPossiveis, value))
	    {
		ViewJogo.drawCharLine('+', 120, true);
		System.out.println("Digitie uma opção válida.");
		ViewJogo.drawCharLine('+', 120, true);
	    }
	    else
		flag = false;

	} while (flag);

	return value;
    }

    private boolean isValidValue(char[] valoresPossiveis, char valorDigitado)
    {
	for (int i = 0; i < valoresPossiveis.length; i++)
	    if (valoresPossiveis[i] == valorDigitado)
		return true;

	return false;
    }

}